﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using JiveMessenger.Models;
using JiveMessenger.User;
using JmShared;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Threading;
using JiveMessenger.Utilities;
using JiveMessenger.CustomEventArgs;
using System.Collections.Specialized;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml;
using Windows.UI.Core;
using System.Threading.Tasks;
using System.Runtime.Serialization;
using JiveMessenger.Log;

namespace JiveMessenger.ViewModels
{
    [DataContract]
    public abstract class ViewModelChat : ViewModelTab
    {
        private object LockObject = new object();
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        private object _lockObject = new object();
        public IClient CurrentClient { get; set; }
        internal IContact CurrentUser
        {
            get
            {
                IContact currentUser = null;
                Messenger.Default.Send(new NotificationMessageAction<IContact>(this, "GetCurrentUserContact", contact =>
                {
                    currentUser = contact;
                }));
                return currentUser;
            }
        }
        protected bool _lastParagraphHadLine;
        private DispatcherTimer LastMessageReceivedTimer { get; set; }
        public IContact LastUserWritten { get; set; }
        public DateTime LastMessageReceivedTime { get; set; }
        public RelayCommand EditFontCommand { get; set; }
        public RelayCommand InputTextTappedTappedCommand { get; set; }
        public RelayCommand<IEmoticon> InsertEmoticonCommand { get; set; }
        public RelayCommand BackspaceCommand { get; set; }

        //public RelayCommand AddEmoticonCommand { get; private set; }
        //public RelayCommand<IEmoticon> EditEmoticonCommand { get; private set; }
        public RelayCommand ChatControlWindowTappedCommand { get; set; }

        public Dictionary<string, DateTime> LastReceivedMessageTyping { get; set; }

        [DataMember]

        private RangeObservableCollection<MessageModel> _messages;
        public RangeObservableCollection<MessageModel> Messages
        {
            get { return _messages; }
            set
            {
                if (_messages != value)
                {
                    Set(() => Messages, ref _messages, value);
                }
            }
        }



        private int _inputTextSelectionStart;
        public int InputTextSelectionStart
        {
            get { return _inputTextSelectionStart; }
            set
            {
                if (_inputTextSelectionStart != value)
                {
                    Set(() => InputTextSelectionStart, ref _inputTextSelectionStart, value);
                }
            }
        }

        private int _inputTextSelectionLength;
        public int InputTextSelectionLength
        {
            get { return _inputTextSelectionLength; }
            set
            {
                if (_inputTextSelectionLength != value)
                {
                    Set(() => InputTextSelectionLength, ref _inputTextSelectionLength, value);
                }
            }
        }

        private string _inputText;
        public string InputText
        {
            get { return _inputText; }
            set
            {
                if (_inputText != value)
                {
                    Set(() => InputText, ref _inputText, value);
                }
            }
        }



        private bool _isNudgeButtonVisible;
        public bool IsNudgeButtonVisible
        {
            get { return _isNudgeButtonVisible; }
            set
            {
                if (_isNudgeButtonVisible != value)
                {
                    Set(() => IsNudgeButtonVisible, ref _isNudgeButtonVisible, value);
                }
            }
        }

        private bool _isInputTextEnabled;
        public bool IsInputTextEnabled
        {
            get { return _isInputTextEnabled; }
            set
            {
                if (_isInputTextEnabled != value)
                {
                    Set(() => IsInputTextEnabled, ref _isInputTextEnabled, value);
                }
            }
        }
        private string _chatControlStatusText;
        public string ChatControlStatusText
        {
            get { return _chatControlStatusText; }
            set
            {
                if (_chatControlStatusText != value)
                {
                    Set(() => ChatControlStatusText, ref _chatControlStatusText, value);
                }
            }
        }
        private bool _isInputTextFocused;
        public bool IsInputTextFocused
        {
            get { return _isInputTextFocused; }
            set
            {
                if (_isInputTextFocused != value)
                {
                    //if (value == true)
                    //{
                    //    ShowEmoticonPopup = false;
                    //}

                    //if (!ShowEmoticonPopup)
                    //    ShowFauxKeyboardGrid = value;


                    //if (!ShowEmoticonPopup && value == false)
                    //    Messenger.Default.Send(new NotificationMessage(this, "RemoveFakeKeyboardAnimation"));
                    //else
                    //    Messenger.Default.Send(new NotificationMessage(this, "ShowFakeKeyboardAnimation"));

                    Set(() => IsInputTextFocused, ref _isInputTextFocused, value);
                    Messenger.Default.Send(new NotificationMessage<bool>(this, value, "IsInputTextFocused"));
                }
            }
        }

        private bool _showFauxKeyboardGrid;
        public bool ShowFauxKeyboardGrid
        {
            get { return _showFauxKeyboardGrid; }
            set
            {
                if (_showFauxKeyboardGrid != value)
                {
                    Set(() => ShowFauxKeyboardGrid, ref _showFauxKeyboardGrid, value);
                }
            }
        }

        private bool _isScrolledToEnd;
        public bool IsScrolledToEnd
        {
            get { return _isScrolledToEnd; }
            set
            {
                if (_isScrolledToEnd != value)
                {
                    Set(() => IsScrolledToEnd, ref _isScrolledToEnd, value);
                }
            }
        }

        private bool _showLoadAnimation;
        public bool ShowLoadAnimation
        {
            get { return _showLoadAnimation; }
            set
            {
                if (_showLoadAnimation != value)
                {
                    Set(() => ShowLoadAnimation, ref _showLoadAnimation, value);
                }
            }
        }

        private double _scrollOffset;
        [DataMember]
        public double ScrollOffset
        {
            get { return _scrollOffset; }
            set
            {
                if (_scrollOffset != value)
                {
                    Set(() => ScrollOffset, ref _scrollOffset, value);
                }
            }
        }


        private int _remainingMessages;
        public int RemainingMessages
        {
            get { return _remainingMessages; }
            set
            {
                if (_remainingMessages != value)
                {
                    Set(() => RemainingMessages, ref _remainingMessages, value);
                }
            }
        }


        public ViewModelChat()
        {

        }

        public ViewModelChat(IClient currentClient, string id, string name)
            : base(id, name)
        {

            SetDefaults();
            this.CurrentClient = currentClient;
            //  this.CurrentUser = currentUser;
            if (currentClient.UserSettings != null)
                this.IsNudgeButtonVisible = currentClient.UserSettings.EnableNudge.GetValueOrDefault();
            //CreateDefaultEmoticonList();
            InitializeCommands();
            StartNowWritingTimer();
        }
        [OnDeserializing]
        internal void PreInitializeChat(StreamingContext context)
        {
            SetDefaults();
        }
        [OnDeserialized]
        internal void InitializeChat(StreamingContext context)
        {
            CurrentClient = VmHelper.Locator.CurrentClient;

            InitializeCommands();
            StartNowWritingTimer();
            if (CurrentClient.UserSettings != null)
                this.IsNudgeButtonVisible = CurrentClient.UserSettings.EnableNudge.GetValueOrDefault();
        }
        private void InitializeCommands()
        {
            this.InputTextTappedTappedCommand = new RelayCommand(InputTextTappedTappedCommandAction);
            this.InsertEmoticonCommand = new RelayCommand<IEmoticon>(InsertEmoticonCommandAction);
            this.BackspaceCommand = new RelayCommand(BackspaceCommandAction);
            this.EditFontCommand = new RelayCommand(EditFontCommandAction, () => CurrentClient.IsLoggedIn);
            //this.AddEmoticonCommand = new RelayCommand(AddEmoticonCommandAction);
            //this.EditEmoticonCommand = new RelayCommand<IEmoticon>(EditEmoticonCommandAction);
            this.ChatControlWindowTappedCommand = new RelayCommand(ChatControlWindowTappedCommandAction);
        }
        private void SetDefaults()
        {
            this.InputText = "";
            this.Messages = new RangeObservableCollection<MessageModel>();
            this.LastReceivedMessageTyping = new Dictionary<string, DateTime>();
            this.IsInputTextEnabled = true;
            this.IsScrolledToEnd = true;
            this.LastUserWritten = null;
            this.LastMessageReceivedTime = DateTime.MinValue;
            this._lastParagraphHadLine = false;
            this.RemainingMessages = 0;
        }
        private void StartNowWritingTimer()
        {
            this.LastMessageReceivedTimer = new DispatcherTimer();
            this.LastMessageReceivedTimer.Tick += this.LastMessageReceivedTimer_Tick;
            this.LastMessageReceivedTimer.Interval = new TimeSpan(0, 0, 1);
            this.LastMessageReceivedTimer.Start();
        }
        public void UpdateMessageQueueStatus(int receivedMessages, int totalMessagesInQueue)
        {
            if (totalMessagesInQueue > 10)
            {
                this.RemainingMessages = totalMessagesInQueue - receivedMessages;
            }
        }
        public void SetStatus(bool offline)
        {
            if (offline)
            {
                IsInputTextEnabled = false;
            }
            else
            {
                IsInputTextEnabled = true;
            }
        }
        private void ChatControlWindowTappedCommandAction()
        {

            // this.ShowEmoticonPopup = false;
            this.IsInputTextFocused = false;
            //  this.ShowFauxKeyboardGrid = false;
            Messenger.Default.Send(new NotificationMessage(this, "HideEmoticonPopup"));
        }
        //private void EditEmoticonCommandAction(IEmoticon e)
        //{
        //    Messenger.Default.Send(new NotificationMessage<IEmoticon>(this, e, "EditEmoticon"));
        //}
        //private void AddEmoticonCommandAction()
        //{
        //    Messenger.Default.Send(new NotificationMessage(this, "AddEmoticon"));
        //}


        private void BackspaceCommandAction()
        {
            int index = this.InputTextSelectionStart - 1;
            if (index > 0)
            {
                this.InputText = InputText.Remove(index, 1);
                this.InputTextSelectionStart = index;
                this.InputTextSelectionLength = 0;
            }
            else
                this.InputText = string.Empty;
        }

        private void InsertEmoticonCommandAction(IEmoticon model)
        {
            //IEmoticon model = args.Model;
            string key = string.Empty;
            if (model.Type == EmoticonType.CustomEmoticon)
            {
                key = ((JiveCustomEmoticonModel)model).EmoticonShortcut.ToString();
            }
            else if (model.Type == EmoticonType.DefaultEmoticon)
            {
                string[] keyArray = ((JiveDefaultEmoticonModel)model).EmoticonShortcut as string[];
                key = keyArray[0];
            }


            InsertEmoticon(key);
          UpdateRecentlyUsedEmoticonList(model);
            //ViewModelLocator locator = App.Current.Resources["Locator"] as ViewModelLocator;
            //if (locator.ViewModelMainPage.EmoticonControlPivotIndex != 2)
            //    CurrentUser.RecentlyUsedEmoticons = recentEmoticons;
        }

        private void InsertEmoticon(string key)
        {
            int offset = InputTextSelectionStart;
            InputTextSelectionLength = 0;
            InputText = InputText.Insert(offset, key);
            InputTextSelectionStart = offset + key.Length;
        }


        private void InputTextTappedTappedCommandAction()
        {
            // this.ShowFauxKeyboardGrid = true;
            this.IsInputTextFocused = true;
            //this.ShowEmoticonPopup = false;

        }
        internal void UpdateRecentlyUsedEmoticonList(IEmoticon model)
        {
            Dictionary<string, int> customEmoticonDic = new Dictionary<string, int>(AppSettings.Instance.RecentlyUsedCustomEmoticons);
            Dictionary<string, int> defaultEmoticonDic = new Dictionary<string, int>(AppSettings.Instance.RecentlyUsedDefaultEmoticons);
            string key = string.Empty;
            if (model.Type == EmoticonType.CustomEmoticon)
            {
                key = ((JiveCustomEmoticonModel)model).EmoticonShortcut.ToString();
                if (!customEmoticonDic.ContainsKey(key))
                {
                    PruneRecentEmoticonList(key, customEmoticonDic, defaultEmoticonDic);
                    customEmoticonDic.Add(key, 0);
                }
                customEmoticonDic[key]++;

                AppSettings.Instance.RecentlyUsedCustomEmoticons = customEmoticonDic;
            }
            else if (model.Type == EmoticonType.DefaultEmoticon)
            {
                string[] keyArray = ((JiveDefaultEmoticonModel)model).EmoticonShortcut as string[];
                key = keyArray[0];

                if (!defaultEmoticonDic.ContainsKey(key))
                {
                    PruneRecentEmoticonList(key, customEmoticonDic, defaultEmoticonDic);
                    defaultEmoticonDic.Add(key, 0);
                }
                defaultEmoticonDic[key]++;

                AppSettings.Instance.RecentlyUsedDefaultEmoticons = defaultEmoticonDic;

            }
            //Todo:Change this to something non-retarded. Sorted collectionview or something. 
            if (model == null)
            {
                if (CurrentUser.RecentlyUsedEmoticons == null)
                    CurrentUser.RecentlyUsedEmoticons = new ObservableCollection<IEmoticon>();
                foreach (var v in customEmoticonDic)
                {
                    model = CurrentUser.CustomEmoticons.FirstOrDefault(x => (string)x.EmoticonShortcut == v.Key);
                    if (!CurrentUser.RecentlyUsedEmoticons.Contains(model))
                        CurrentUser.RecentlyUsedEmoticons.Add(model);
                }
                foreach (var v in defaultEmoticonDic)
                {
                    model = CurrentClient.DefaultEmoticons.FirstOrDefault(x => ((string[])x.EmoticonShortcut).Contains(v.Key));
                    if (!CurrentUser.RecentlyUsedEmoticons.Contains(model))
                        CurrentUser.RecentlyUsedEmoticons.Add(model);
                }
            }
            else
            {
                if (!CurrentUser.RecentlyUsedEmoticons.Contains(model))
                    CurrentUser.RecentlyUsedEmoticons.Add(model);
            }
            if (CurrentUser.RecentlyUsedEmoticons == null)
                return;
            var sortedList = CurrentUser.RecentlyUsedEmoticons.ToList();

            sortedList.Sort((x, y) =>
             {
                 int thisCount = 0;
                 if (x.Type == EmoticonType.CustomEmoticon)
                     thisCount = customEmoticonDic[(string)((JiveCustomEmoticonModel)x).EmoticonShortcut];
                 else if (x.Type == EmoticonType.DefaultEmoticon)
                 {
                     var shortcut = (string[])((JiveDefaultEmoticonModel)x).EmoticonShortcut;
                     thisCount = defaultEmoticonDic.Where(emoticon => shortcut.Contains(emoticon.Key)).First().Value;
                 }

                 int otherCount = 0;

                 if (y.Type == EmoticonType.CustomEmoticon)
                     otherCount = customEmoticonDic[(string)((JiveCustomEmoticonModel)y).EmoticonShortcut];
                 else if (y.Type == EmoticonType.DefaultEmoticon)
                 {
                     var shortcut = (string[])((JiveDefaultEmoticonModel)y).EmoticonShortcut;
                     otherCount = defaultEmoticonDic.Where(emoticon => shortcut.Contains(emoticon.Key)).First().Value;
                 }



                 if (thisCount == otherCount)
                     return 0;
                 else if (thisCount > otherCount)
                     return -1;
                 else
                     return 1;

             });
            int index = 0;
            ViewModelLocator locator = App.Current.Resources["Locator"] as ViewModelLocator;
            if (locator.ViewModelMainPage.EmoticonControlPivotIndex != 2)
            {
                while (index < sortedList.Count)
                {
                    if (!CurrentUser.RecentlyUsedEmoticons[index].Equals(sortedList[index]))
                    {
                        IEmoticon t = CurrentUser.RecentlyUsedEmoticons[index];
                        CurrentUser.RecentlyUsedEmoticons.Move(index, sortedList.IndexOf(t));
                        //CurrentUser.RecentlyUsedEmoticons.RemoveAt(index);
                        //CurrentUser.RecentlyUsedEmoticons.Insert(sortedList.IndexOf(t), t);
                    }
                    else
                    {
                        index++;
                    }
                }
            }
        }

        private void PruneRecentEmoticonList(string key, Dictionary<string, int> customEmoticonDic, Dictionary<string, int> defaultEmoticonDic)
        {

            if (CurrentUser.RecentlyUsedEmoticons.Count >= 20)
            {
                IEmoticon emoticon = CurrentUser.RecentlyUsedEmoticons.LastOrDefault(x =>
                {
                    if (x.Type == EmoticonType.CustomEmoticon)
                    {
                        if (((JiveCustomEmoticonModel)x).EmoticonShortcut != key)
                            return true;
                    }
                    if (x.Type == EmoticonType.DefaultEmoticon)
                    {
                        if (((JiveDefaultEmoticonModel)x).EmoticonShortcut[0] != key)
                            return true;
                    }
                    return false;
                });
                CurrentUser.RecentlyUsedEmoticons.Remove(emoticon);
                if (emoticon.Type == EmoticonType.DefaultEmoticon)
                    defaultEmoticonDic.Remove(((JiveDefaultEmoticonModel)emoticon).EmoticonShortcut[0]);
                else if (emoticon.Type == EmoticonType.CustomEmoticon)
                    customEmoticonDic.Remove(((JiveCustomEmoticonModel)emoticon).EmoticonShortcut);

            }
        }

        /// <summary>
        /// Timer that updates txtBlockNowWriting
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LastMessageReceivedTimer_Tick(object sender, object e)
        {
            DispatcherTimer timer = (DispatcherTimer)sender;
            timer.Stop();
            try
            {
                bool clear = true;
                if (this.LastUserWritten == null)
                    return;
                string isWritingText = "";
                bool someoneWriting = false;

                foreach (KeyValuePair<string, DateTime> kvp in this.LastReceivedMessageTyping)
                {
                    if ((DateTime.Now.Subtract(kvp.Value).TotalSeconds > 3))
                        continue;

                    someoneWriting = true;
                    Messenger.Default.Send(new NotificationMessageAction<IContact>(this, kvp.Key, ViewModelContactList.MessengerCommands.GetContactFromUserID.ToString(), contact =>
                    {
                        if (contact != null)
                            isWritingText += contact.PrettyName;
                    }));
                    //if (CurrentClient.ConnectedClients.ContainsKey(kvp.Key))
                    //    isWritingText += CurrentClient.ConnectedClients[kvp.Key].PrettyName + ",";

                }
                if (someoneWriting)
                    clear = false;

                if (!String.IsNullOrEmpty(isWritingText) && !clear)
                {
                    isWritingText = isWritingText.TrimEnd(',');
                    isWritingText += " is writing.";
                }
                if (String.IsNullOrEmpty(isWritingText))
                    isWritingText = String.Format("Last message received at {0} on {1}.", this.LastMessageReceivedTime.ToShortTimeString(), this.LastMessageReceivedTime.ToShortDateString());//

                this.ChatControlStatusText = isWritingText;
            }
            catch (Exception err)
            {
                Logger.ErrorException("Error in last message received timer", err);
            }
            finally
            {
                timer.Start();
            }
        }
        private MessageModel CreateMessageModel(DisplayMessage displayMessage)
        {
            DisplayMessageOptions displayOptions = displayMessage.MessageOptions;
            string message = displayMessage.Message;
            IContact sender = displayMessage.Sender;
            DateTime timestamp = displayMessage.TimeStamp;
            string chatTarget = displayMessage.ChatTarget;
            bool showInfoTimeStamp = displayOptions.ShowInfoTimeStamp;
            bool greenText = displayOptions.GreenText;
            bool redText = displayOptions.RedText;
            bool addLine = displayOptions.AddLine; ;
            bool addSpacer = displayOptions.AddSpacer;
            bool isLastInBatch = displayMessage.IsLastInBatch;

            bool showSender = displayOptions.ShowSender;// && !greenText && !redText;


            MessageModelType type = MessageModelType.Incoming;
            if (greenText || redText)
                type = MessageModelType.SystemMessage;
            else if (sender.UserName == CurrentUser.UserName)
                type = MessageModelType.Outgoing;

            var model = new MessageModel(sender, timestamp, type)
            {
                IsUserNameVisible = showSender,
                MessageText = message,
                IsLastInBatch = isLastInBatch
            };
            return model;

        }
        public virtual void DisplayMessage(DisplayMessage displayMessage)
        {

            this.Messages.Add(CreateMessageModel(displayMessage));


            LastMessageReceivedTime = displayMessage.TimeStamp;
            if (!IsInDesignMode)
            {
                if (displayMessage.MessageOptions.BlinkTab && !this.IsCurrentActiveTab)
                    this.IsBlinking = true;
            }
            LastUserWritten = displayMessage.Sender;
        }
        /// <summary>
        /// Bulk insert a list of DisplayMessages. This is faster when adding a lot of messages.
        /// </summary>
        /// <param name="displayMessages"></param>
        public virtual void DisplayMessage(List<DisplayMessage> displayMessages)
        {
            if (displayMessages == null || displayMessages.Count == 0)
                return;
            List<MessageModel> models = new List<MessageModel>();
            foreach (DisplayMessage displayMessage in displayMessages)
            {
                models.Add(CreateMessageModel(displayMessage));
                LastUserWritten = displayMessage.Sender;
            }
            this.Messages.AddRange(models);
            var lastMessage = displayMessages.Last();
            LastMessageReceivedTime = lastMessage.TimeStamp;

            if (!IsInDesignMode)
            {
                if (lastMessage.MessageOptions.BlinkTab && !this.IsCurrentActiveTab)
                    this.IsBlinking = true;
            }
        }



        public override void Cleanup()
        {
            if (this.LastMessageReceivedTimer != null)
            {
                this.LastMessageReceivedTimer.Stop();
                this.LastMessageReceivedTimer = null;
            }

            // this.CustomEmoticons = null;
            //this.DefaultEmoticons = null;
            base.Cleanup();
        }
        private void EditFontCommandAction()
        {
            Messenger.Default.Send<NotificationMessage>(new NotificationMessage(this, "ChangeFont"));
        }
        public override int GetHashCode()
        {
            return this.ID.GetHashCode();
        }
        public bool Equals(ViewModelChat other)
        {
            return this.ID == other.ID;
        }
        public override bool Equals(object obj)
        {
            if (obj is ViewModelChat)
                return Equals((ViewModelChat)obj);
            else
                return base.Equals(obj);
        }

    }
}
